/*
 *
 * Copyright(c) 2010 Safelayer Secure Communications
 *
 * This is open source software, placed under the terms of the
 * GNU GENERAL PUBLIC LICENSE Version 3.
 * Please see: http://www.gnu.org/licenses/gpl-3.0.txt
 * 
 * Permission to use, copy, modify, and distribute this software
 * for any purpose and without fee is hereby granted, provided
 * that the above copyright notices appear in all copies and that
 * both the copyright notice and this permission notice appear in
 * supporting documentation.
 * 
 */
package com.neuralnoise.foaf.core;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

/**
 * Models an OWL Thing. OWL introduces the class <code>Thing</code> as a name for the universal class
 * of all things. This is sometimes useful when expressing universality of property use
 * <p>
 * For more info see FOAF Vocabulary Specification
 * 
 * @author victor.martinez <swtp@safelayer.com>
 */
public class Thing implements ThingNonStandard {

	/** Works as identifier */
	private String uri; // not included in the FOAF specification

	private Thing seeAlso; // not included in the FOAF specification

	private Set<String> nameSet = new LinkedHashSet<String>();

	private Set<Image> depictionSet = new LinkedHashSet<Image>();

	private Set<Document> homepageSet = new LinkedHashSet<Document>();

	private Set<Document> isPrimaryTopicOfSet = new LinkedHashSet<Document>();

	private Set<Thing> logoSet = new LinkedHashSet<Thing>();

	private Set<Agent> makerSet = new LinkedHashSet<Agent>();

	private Set<Document> pageSet = new LinkedHashSet<Document>();

	public Thing(String uri) {
		this.uri = uri;
	}

	/**
	 * Returns all names.
	 * 
	 * @return Returns all names.
	 */
	public List<String> getAllNames() {
		return new ArrayList<String>(nameSet);
	}

	/**
	 * Adds the specified <code>name</code> to this {@link Thing} if it is not already present.
	 * 
	 * @param name The <code>name</code> to be added to this <code>Thing</code>.
	 * @return <tt>true</tt> if the <code>Thing</code> did not already contain the specified
	 *         <code>name</code>.
	 */
	public boolean addName(String name) {
		return nameSet.add(name);
	}

	/**
	 * Removes the specified <code>name</code> from this {@link Thing}, if it is present. Returns
	 * <tt>true</tt> if this <code>Thing</code> contained the specified <code>name</code> (or
	 * equivalently, if this <code>Thing</code> changed as a result of the call).
	 * 
	 * @param name The <code>name</code> to be removed from this <code>Thing</code>.
	 * 
	 * @return <tt>true</tt> if this <code>Thing</code> contained the specified <code>name</code>.
	 */
	public boolean removeName(String name) {
		return nameSet.remove(name);
	}

	/**
	 * Removes all of the names from this {@link Thing}. The names from <code>Thing</code> will be
	 * empty after this call returns.
	 */
	public void clearNames() {
		nameSet.clear();
	}

	/**
	 * Returns <tt>true</tt> if this {@link Thing} contains the specified <code>name</code>.
	 * 
	 * @param name The <code>name</code> whose presence in this <code>Thing</code> is to be tested.
	 * @return <tt>true</tt> if this <code>Thing</code> contains the specified <code>name</code>.
	 */
	public boolean containsName(String name) {
		return nameSet.contains(name);
	}

	/**
	 * Returns all depictions.
	 * 
	 * @return Returns all depictions.
	 */
	public List<Image> getAllDepictions() {
		return new ArrayList<Image>(depictionSet);
	}

	/**
	 * Adds the specified <code>depiction</code> to this {@link Thing} if it is not already present.
	 * 
	 * @param depiction The <code>depiction</code> to be added to this <code>Thing</code>.
	 * @return <tt>true</tt> if the <code>Thing</code> did not already contain the specified
	 *         <code>depiction</code>.
	 */
	public boolean addDepiction(Image depiction) {
		return depictionSet.add(depiction);
	}

	/**
	 * Removes the specified <code>depiction</code> from this {@link Thing}, if it is present. Returns
	 * <tt>true</tt> if this <code>Thing</code> contained the specified <code>depiction</code> (or
	 * equivalently, if this <code>Thing</code> changed as a result of the call).
	 * 
	 * @param depiction The <code>depiction</code> to be removed from this <code>Thing</code>.
	 * 
	 * @return <tt>true</tt> if this <code>Thing</code> contained the specified
	 *         <code>depiction</code>.
	 */
	public boolean removeDepiction(Image depiction) {
		return depictionSet.remove(depiction);
	}

	/**
	 * Removes all of the depictions from this {@link Thing}. The depictions from <code>Thing</code>
	 * will be empty after this call returns.
	 */
	public void clearDepictions() {
		depictionSet.clear();
	}

	/**
	 * Returns <tt>true</tt> if this {@link Thing} contains the specified <code>depiction</code>.
	 * 
	 * @param depiction The <code>depiction</code> whose presence in this <code>Thing</code> is to
	 *            be tested.
	 * @return <tt>true</tt> if this <code>Thing</code> contains the specified
	 *         <code>depiction</code>.
	 */
	public boolean containsDepiction(Image depiction) {
		return depictionSet.contains(depiction);
	}

	/**
	 * Returns all homepages.
	 * 
	 * @return Returns all homepages.
	 */
	public List<Document> getAllHomepages() {
		return new ArrayList<Document>(homepageSet);
	}

	/**
	 * Removes all of the homepages from this {@link Thing}. The homepages from <code>Thing</code>
	 * will be empty after this call returns.
	 */
	public void clearHomepages() {
		homepageSet.clear();
	}

	/**
	 * Adds the specified <code>homepage</code> to this {@link Thing} if it is not already present.
	 * 
	 * @param homepage The <code>homepage</code> to be added to this <code>Thing</code> .
	 * @return <tt>true</tt> if the <code>Thing</code>. did not already contain the specified
	 *         <code>homepage</code>.
	 */
	public boolean addHomepage(Document homepage) {
		return homepageSet.add(homepage);
	}

	/**
	 * Removes the specified <code>homepage</code> from this {@link Thing}, if it is present. Returns
	 * <tt>true</tt> if this <code>Thing</code> contained the specified <code>homepage</code> (or
	 * equivalently, if this <code>Thing</code> changed as a result of the call).
	 * 
	 * @param homepage The <code>homepage</code> to be removed from this <code>Thing</code>.
	 * 
	 * @return <tt>true</tt> if this <code>Thing</code> contained the specified
	 *         <code>homepage</code>.
	 */
	public boolean removeHomepage(Document homepage) {
		return homepageSet.remove(homepage);
	}

	/**
	 * Returns <tt>true</tt> if this {@link Thing} contains the specified homepage.
	 * 
	 * @param homepage The <code>homepage</code> whose presence in this <code>Thing</code> is to be
	 *            tested.
	 * @return <tt>true</tt> if this <code>Thing</code> contains the specified <code>homepage</code>
	 *         .
	 */
	public boolean containsHomepage(Document homepage) {
		return homepageSet.contains(homepage);
	}

	/**
	 * Returns all isPrimaryTopicOfs.
	 * 
	 * @return Returns all isPrimaryTopicOfs.
	 */
	public List<Document> getAllIsPrimaryTopicOfs() {
		return new ArrayList<Document>(isPrimaryTopicOfSet);
	}

	/**
	 * Removes all of the isPrimaryTopicOfs from this {@link Thing}. The isPrimaryTopicOfs from
	 * <code>Thing</code> will be empty after this call returns.
	 */
	public void clearIsPrimaryTopicOfs() {
		isPrimaryTopicOfSet.clear();
	}

	/**
	 * Adds the specified <code>isPrimaryTopicOf</code> to this {@link Thing} if it is not already
	 * present.
	 * 
	 * @param isPrimaryTopicOf The <code>isPrimaryTopicOf</code> to be added to this
	 *            <code>Thing</code> .
	 * @return <tt>true</tt> if the <code>Thing</code> did not already contain the specified
	 *         <code>isPrimaryTopicOf</code>.
	 */
	public boolean addIsPrimaryTopicOf(Document isPrimaryTopicOf) {
		return isPrimaryTopicOfSet.add(isPrimaryTopicOf);
	}

	/**
	 * Removes the specified <code>isPrimaryTopicOf</code> from this {@link Thing}, if it is present.
	 * Returns <tt>true</tt> if this <code>Thing</code> contained the specified
	 * <code>isPrimaryTopicOf</code> (or equivalently, if this <code>Thing</code> changed as a
	 * result of the call).
	 * 
	 * @param isPrimaryTopicOf The <code>isPrimaryTopicOf</code> to be removed from this <code>Thing</code>.
	 * 
	 * @return <tt>true</tt> if this <code>Thing</code> contained the specified
	 *         <code>isPrimaryTopicOf</code>.
	 */
	public boolean removeIsPrimaryTopicOf(Document isPrimaryTopicOf) {
		return isPrimaryTopicOfSet.remove(isPrimaryTopicOf);
	}

	/**
	 * Returns <tt>true</tt> if this {@link Thing} contains the specified isPrimaryTopicOf.
	 * 
	 * @param isPrimaryTopicOf The <code>isPrimaryTopicOf</code> whose presence in this
	 *            <code>Thing</code> is to be tested.
	 * @return <tt>true</tt> if this <code>Thing</code> contains the specified
	 *         <code>isPrimaryTopicOf</code>.
	 */
	public boolean containsIsPrimaryTopicOf(Document isPrimaryTopicOf) {
		return isPrimaryTopicOfSet.contains(isPrimaryTopicOf);
	}

	/**
	 * Returns all logos.
	 * 
	 * @return Returns all logos.
	 */
	public List<Thing> getAlllogos() {
		return new ArrayList<Thing>(logoSet);
	}

	/**
	 * Adds the specified <code>logo</code> to this {@link Thing} if it is not already present.
	 * 
	 * @param logo The <code>logo</code> to be added to this <code>Thing</code>.
	 * @return <tt>true</tt> if the <code>Thing</code> did not already contain the specified
	 *         <code>logo</code>.
	 */
	public boolean addLogo(Thing logo) {
		return logoSet.add(logo);
	}

	/**
	 * Removes the specified <code>logo</code> from this {@link Thing}, if it is present. Returns
	 * <tt>true</tt> if this <code>Thing</code> contained the specified <code>logo</code> (or
	 * equivalently, if this <code>Thing</code> changed as a result of the call).
	 * 
	 * @param logo The <code>logo</code> to be removed from this <code>Thing</code>.
	 * 
	 * @return <tt>true</tt> if this <code>Thing</code> contained the specified <code>logo</code>.
	 */
	public boolean removeLogo(Thing logo) {
		return logoSet.remove(logo);
	}

	/**
	 * Removes all of the logos from this {@link Thing}. The logos from <code>Thing</code> will be
	 * empty after this call returns.
	 */
	public void clearLogos() {
		logoSet.clear();
	}

	/**
	 * Returns <tt>true</tt> if this {@link Thing} contains the specified <code>logo</code>.
	 * 
	 * @param logo The <code>logo</code> whose presence in this <code>Thing</code> is to be tested.
	 * @return <tt>true</tt> if this <code>Thing</code> contains the specified <code>logo</code>.
	 */
	public boolean containsLogo(Thing logo) {
		return logoSet.contains(logo);
	}

	/**
	 * Returns all makers.
	 * 
	 * @return Returns all makers.
	 */
	public List<Agent> getAllMakers() {
		return new ArrayList<Agent>(makerSet);
	}

	/**
	 * Adds the specified <code>maker</code> to this {@link Thing} if it is not already present.
	 * 
	 * @param maker The <code>maker</code> to be added to this <code>Thing</code>.
	 * @return <tt>true</tt> if the <code>Thing</code>. did not already contain the specified
	 *         <code>maker</code>.
	 */
	public boolean addMaker(Agent maker) {
		return makerSet.add(maker);
	}

	/**
	 * Removes the specified <code>maker</code> from this {@link Thing}, if it is present. Returns
	 * <tt>true</tt> if this <code>Thing</code> contained the specified <code>maker</code> (or
	 * equivalently, if this <code>Thing</code> changed as a result of the call).
	 * 
	 * @param maker The <code>maker</code> to be removed from this <code>Thing</code>.
	 * 
	 * @return <tt>true</tt> if this <code>Thing</code> contained the specified <code>maker</code>.
	 */
	public boolean removeMaker(Agent maker) {
		return makerSet.remove(maker);
	}

	/**
	 * Removes all of the makers from this {@link Thing}. The makers from <code>Thing</code> will be
	 * empty after this call returns.
	 */
	public void clearmakers() {
		makerSet.clear();
	}

	/**
	 * Returns <tt>true</tt> if this {@link Thing} contains the specified <code>maker</code>.
	 * 
	 * @param maker The <code>maker</code> whose presence in this <code>Thing</code> is to be
	 *            tested.
	 * @return <tt>true</tt> if this <code>Thing</code> contains the specified <code>maker</code>.
	 */
	public boolean containsMaker(Agent maker) {
		return makerSet.contains(maker);
	}
	

	public String getUri() {
		return uri;
	}

	/**
	 * @param uri The uri to set.
	 */
	public void setUri(String uri) {
		this.uri = uri;
	}

	/**
	 * Return all pages.
	 * 
	 * @return Returns all pages.
	 */
	public List<Document> getAllPages() {
		return new ArrayList<Document>(pageSet);
	}

	/**
	 * Removes all of the pages from this {@link Thing}. The pages from <code>Thing</code> will be
	 * empty after this call returns.
	 */
	public void clearPages() {
		pageSet.clear();
	}

	/**
	 * Adds the specified <code>page</code> to this {@link Thing} if it is not already present.
	 * 
	 * @param page The <code>page</code> to be added to this <code>Thing</code> .
	 * @return <tt>true</tt> if the <code>Thing</code>. did not already contain the specified
	 *         <code>page</code>.
	 */
	public boolean addPage(Document page) {
		return pageSet.add(page);
	}

	/**
	 * Removes the specified <code>page</code> from this {@link Thing}, if it is present. Returns
	 * <tt>true</tt> if this <code>Thing</code> contained the specified <code>page</code> (or
	 * equivalently, if this <code>Thing</code> changed as a result of the call).
	 * 
	 * @param page The <code>page</code> to be removed from this <code>Thing</code>.
	 * 
	 * @return <tt>true</tt> if this <code>Thing</code> contained the specified <code>page</code>.
	 */
	public boolean removePage(Document page) {
		return pageSet.remove(page);
	}

	/**
	 * Returns <tt>true</tt> if this {@link Thing} contains the specified page.
	 * 
	 * @param page The <code>page</code> whose presence in this <code>Thing</code> is to be tested.
	 * @return <tt>true</tt> if this <code>Thing</code> contains the specified <code>page</code>.
	 */
	public boolean containsPage(Document page) {
		return pageSet.contains(page);
	}

	/**
	 * @return the seeAlso.
	 */
	public Thing getSeeAlso() {
		return seeAlso;
	}

	/**
	 * @param seeAlso The seeAlso to set.
	 */
	public void setSeeAlso(Thing seeAlso) {
		this.seeAlso = seeAlso;
	}

	/**
	 * @see java.lang.Object#hashCode().
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((uri == null) ? 0 : uri.hashCode());
		return result;
	}

	/**
	 * Determine whether two things are equal, that is if their URIs are equal since URIs work as
	 * identifiers.
	 * 
	 * @param obj The object to be compared.
	 * @return true if and only if both objects are equal.
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Thing))
			return false;
		Thing other = (Thing) obj;
		if (uri == null || other.uri == null)
			return false;
		if (!uri.equals(other.uri))
			return false;
		return true;
	}

}
